package team8.paacdroid;

import java.io.IOException;
import java.net.MalformedURLException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import team8.paacdroid.activity.RootActivity;
import team8.paacdroid.activity.ViewItineraryActivity;
import team8.paacdroid.bean.BusRide;
import team8.paacdroid.bean.Itinerary;

import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.Spinner;

public class PAACdroid extends RootActivity {
	// UI elements declarations
	private Button btnSearchTripPlanner;
	private EditText actvOrigin, actvDestination;
	private CheckBox cbAdvancedOptions;
	private LinearLayout llAdvancedOptions;
	private Spinner spnMaxWalkDistance, spnTripPreference, spnNumOfItinerary;
	private RadioButton radioDeparture, radioArrival;
	
	// Program parameters
	final String BASE_URL = "http://www.portauthority.org/atisdnn/";
	final String WEB_ENDPOINT = "default.aspx";
	final boolean OFFLINE_MODE = true;
	
	// Var declarations
	private HashMap<String, String> queryString = new HashMap<String, String>();
							   
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Load the layout
		setContentView(R.layout.main);
		btnSearchTripPlanner = (Button) this.findViewById(R.id.btnSearchTripPlanner);
		actvOrigin = (EditText) this.findViewById(R.id.actvOrigin);
		actvDestination = (EditText) this.findViewById(R.id.actvDestination);
		cbAdvancedOptions = (CheckBox) this.findViewById(R.id.cbAdvancedOptions);
		llAdvancedOptions = (LinearLayout) this.findViewById(R.id.llAdvancedOptions);
		spnMaxWalkDistance = (Spinner) this.findViewById(R.id.spnMaxWalkDistance);
		spnTripPreference = (Spinner) this.findViewById(R.id.spnTripPreference);
		spnNumOfItinerary = (Spinner) this.findViewById(R.id.spnNumOfItinerary);
		radioDeparture = (RadioButton) this.findViewById(R.id.radioDeparture);
		radioArrival = (RadioButton) this.findViewById(R.id.radioArrival);
		
		// Populate the spinners/dropdown menus
		// TODO Have this parameterized outside of this .java code
		String[] arrMaxWalkDistance = {"1/8 Mile", "1/4 Mile", "1/2 Mile", "3/4 Mile", "1 Mile"};
		String[] arrTripPreference = {"Quickest", "Fewest Transfers", "Least Walking"};
		String[] arrNumOfItinerary = {"1", "3", "6"};
		HashMap<String, String> hshMaxWalkDistance = new HashMap<String, String>();
		HashMap<String, String> hshTripPreference = new HashMap<String, String>();
		HashMap<String, String> hshNumOfItinerary = new HashMap<String, String>();
		hshMaxWalkDistance.put("1/8 Mile", ".12");
		hshMaxWalkDistance.put("1/4 Mile", ".25");
		hshMaxWalkDistance.put("1/2 Mile", ".50");
		hshMaxWalkDistance.put("3/4 Mile", ".75");
		hshMaxWalkDistance.put("1 Mile", ".9999");
		hshTripPreference.put("Quickest", "T");
		hshTripPreference.put("Fewest Transfers", "X");
		hshTripPreference.put("Least Walking", "W");
		hshNumOfItinerary.put("1", "1");
		hshNumOfItinerary.put("3", "3");
		hshNumOfItinerary.put("6", "6");
		ArrayAdapter<String> adptMaxWalkDistance = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, arrMaxWalkDistance);
		ArrayAdapter<String> adptTripPreference = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, arrTripPreference);
		ArrayAdapter<String> adptNumOfItinerary = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, arrNumOfItinerary);
		adptMaxWalkDistance.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		adptTripPreference.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		adptNumOfItinerary.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		spnMaxWalkDistance.setAdapter(adptMaxWalkDistance);
		spnTripPreference.setAdapter(adptTripPreference);
		spnNumOfItinerary.setAdapter(adptNumOfItinerary);
		spnMaxWalkDistance.setSelection(2);
		spnTripPreference.setSelection(0);
		spnNumOfItinerary.setSelection(2);
	}

	public void doSearchRoute(View button) {
		try {
			// TODO Get a fresh VIEWSTATE everytime
			String viewState = getResources().getText(R.string.view_state_trip_planner).toString();
			
			// Get the results
			HashMap<String, Object> result = processHttpResponse(createAndSendTripPlannerHttpRequest(viewState));
			
			// Handle the result
			handleResult(result);
		} catch (MalformedURLException e) {
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("An error had occured: MalformedURLException.");
			AlertDialog alert = builder.create();
			alert.show();
			Log.e(getString(R.string.app_name), "MalformedURLException: " + e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("An error had occured: IOException.");
			AlertDialog alert = builder.create();
			alert.show();
			Log.e(getString(R.string.app_name), "IOException: " + e.getMessage());
			e.printStackTrace();
		} catch (ParseException e) {
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("An error had occured: ParseException.");
			AlertDialog alert = builder.create();
			alert.show();
			Log.e(getString(R.string.app_name), "ParseException: " + e.getMessage());
			e.printStackTrace();
		}
	}
	
	public String createAndSendTripPlannerHttpRequest(String viewState) throws MalformedURLException, IOException {
		String result;
		
		// Get the travel time & date
		String strHour = "";
		String strMinute = "";
		String strAmPm = "";
		String strDate = "";
		if (llAdvancedOptions.getVisibility() == View.VISIBLE) {
			// TODO If the advanced options panel is visible, get time and date from the widgets
			
		} else {
			// If the advanced options panel is invisible, get current time and date
			Date now = new Date();
			Calendar cal = Calendar.getInstance();
			SimpleDateFormat sdf1 = new SimpleDateFormat("M/d/yyyy");
			cal.setTime(now);
			strHour = ((Integer) cal.get(Calendar.HOUR)).toString();
			strMinute = ((Integer) cal.get(Calendar.MINUTE)).toString();
			strAmPm = cal.get(Calendar.AM_PM) == 1? "PM" : "AM";
			strDate = sdf1.format(now);
		}
		// The minutes are rounded up or down in increments of 5 minutes.
		// TODO Forward the time 10 minutes earlier so that we can show earlier buses, if available.
		Integer tmpMinute = Integer.parseInt(strMinute);
		if (tmpMinute%5>2) {
			tmpMinute += 5-(tmpMinute%5);
		} else {
			tmpMinute = (Integer) tmpMinute/5;
			tmpMinute *= 5;
		}
		strMinute = tmpMinute.toString();
		queryString.put("ddlHours", strHour);
		queryString.put("ddlMinutes", strMinute);
		queryString.put("ddlAmPm", strAmPm);
		queryString.put("hdnTripDate", strDate);
		
		// Get the origin address/intersection
		queryString.put("txtDeparture", actvOrigin.getText().toString());
		
		// Get the origin type (address/intersection)
		queryString.put("radDeparture", "I");
		
		// Get the destination address/intersection
		queryString.put("txtArrival", actvDestination.getText().toString());
		
		// Get the origin type (address/intersection)
		queryString.put("radArrival", "I");
		
		// TODO Get the time of arrival/departure
		if (llAdvancedOptions.getVisibility() == View.VISIBLE) {
			// If the advanced options panel is visible, get this from the widget
			
		} else {
			queryString.put("ddlArrDep", "D");
		}
		
		// TODO Get the number of itineraries
		if (llAdvancedOptions.getVisibility() == View.VISIBLE) {
			// If the advanced options panel is visible, get this from the widget
			
		} else {
			queryString.put("ddlNumItins", "6");
		}
		
		// TODO Get the trip preference
		if (llAdvancedOptions.getVisibility() == View.VISIBLE) {
			// If the advanced options panel is visible, get this from the widget
			
		} else {
			queryString.put("ddlTripPreference", "T");
		}
		
		// Get the max walking distance
		if (llAdvancedOptions.getVisibility() == View.VISIBLE) {
			// If the advanced options panel is visible, get this from the widget
			
		} else {
			queryString.put("ddlWalkDist", ".25");
		}
		
		// Add hidden fields to coax the results from the webform
		queryString.put("__VIEWSTATE", viewState);
		queryString.put("btnSubmit", "Submit");
		
		// Send a HTTP GET/POST request
//		result = sendHttpGetRequest(BASE_URL + WEB_ENDPOINT, queryString);
		if (!OFFLINE_MODE) {
			result = sendHttpPostRequest(BASE_URL + WEB_ENDPOINT, queryString);
		} else {
			result = "";
		}
		
		// Return result
		return result;
	}
	
	@SuppressWarnings("unchecked")
	public HashMap<String, Object> processHttpResponse(String response) throws ParseException {
		HashMap<String, Object> result = new HashMap<String, Object>();
		
	if (!OFFLINE_MODE) {
			// Prepare regex patterns for result determination
			Pattern patRealResult = Pattern.compile("<span id=\\\"lblItineraryHeader\\\">You Have (\\d+) Itineraries to Choose From</span></h2>");
			Pattern patNoOrigin = Pattern.compile("<span id=\"lblError\" class=\"ErrorLabel\">(Please enter Departure information.|An error has occurred with the departure location in your trip schedule.<BR>Please resubmit your trip plan.)</span>");
			Pattern patNoDestination = Pattern.compile("<span id=\"lblError\" class=\"ErrorLabel\">Please enter Arrival information.</span>");
			Pattern patNoTravelDate = Pattern.compile("<span id=\"lblError\" class=\"ErrorLabel\">Please enter a valid travel date \\(mm/dd/yyyy\\).</span>");
			Pattern patNoStopsWithinWalkingDist = Pattern.compile("<span id=\"lblError\" class=\"ErrorLabel\">No stops within walking distance of destination</span>");
			Pattern patUnknownOrigin = Pattern.compile("<TABLE id=\"tblClarifyDeparture\" cellSpacing=\"0\" cellPadding=\"3\" width=\"100%\" border=\"0\">");
			Pattern patUnknownDestination = Pattern.compile("<TABLE id=\"tblClarifyArrival\" cellSpacing=\"0\" cellPadding=\"3\" width=\"100%\" border=\"0\">");
			Pattern patTrivialDistance = Pattern.compile("<span id=\"lblError\" class=\"ErrorLabel\">Origin is within trivial distance of the destination</span>");
			Pattern patTripNotPossible = Pattern.compile("<span id=\"lblError\" class=\"ErrorLabel\">Trip not possible</span>");
			Matcher matRealResult = patRealResult.matcher(response);
			Matcher matNoOrigin = patNoOrigin.matcher(response);
			Matcher matNoDestination = patNoDestination.matcher(response);
			Matcher matNoTravelDate = patNoTravelDate.matcher(response);
			Matcher matNoStopsWithinWalkingDist = patNoStopsWithinWalkingDist.matcher(response);
			Matcher matUnknownOrigin = patUnknownOrigin.matcher(response);
			Matcher matUnknownDestination = patUnknownDestination.matcher(response);
			Matcher matTrivialDistance = patTrivialDistance.matcher(response);
			Matcher matTripNotPossible = patTripNotPossible.matcher(response);
			
			// Determine the type of the result
			if (matRealResult.find()) {
				// If the result is a list of itineraries (intended result)
				result = grabItineraries(response);
			} else if (matNoOrigin.find()) {
				// If the result is an error because we left out the origin address in the form
				result.put("Type", "Error");
				result.put("Message", "Please specify origin address/intersection.");
			} else if (matNoDestination.find()) {
				// If the result is an error because we left out the destination address in the form
				result.put("Type", "Error");
				result.put("Message", "Please specify destination address/intersection.");
			} else if (matNoTravelDate.find()) {
				// If the result is an error because we left out the travel date in the form
				result.put("Type", "Error");
				result.put("Message", "Please specify travel date.");
			} else if (matNoStopsWithinWalkingDist.find()) {
				// If the result is an error because there are no stops available to service the given addresses
				result.put("Type", "Error");
				result.put("Message", "No stops within walking distance.");
			} else if(matUnknownOrigin.find()) {
				// If the result is unknown origin (PAAC system will return a list of suggestions)
				result = grabAddressSuggestions("Origin", response);
			} else if(matUnknownDestination.find()) {
				// If the result is unknown destination (PAAC system will return a list of suggestions)
				result = grabAddressSuggestions("Destination", response);
			} else if(matTrivialDistance.find()) {
				// If the result is an error because of the origin & destination is of trivial distance
				result.put("Type", "Error");
				result.put("Message", "Origin & destination addresses/intersections are within trivial distance.");
			} else if(matTripNotPossible.find()) {
				// If the result is an error because of the origin & destination is of trivial distance
				result.put("Type", "Error");
				result.put("Message", "Trip not possible.");
			} else {
				// If the result is none of the above
				result.put("Type", "Error");
				result.put("Message", "Unknown response.");
			}
		} else {
			result.put("Type", "Itinerary");
			result.put("Itineraries", createFallBackPlan());
		}
	
		// Return response
		return result;
	}
	
	private HashMap<String, Object> grabAddressSuggestions(String addressType, String response) {
		HashMap<String, Object> result = new HashMap<String, Object>();
		
		// Add a result label
		result.put("Type", "Suggestions");
		
		// Add the address type to the result
		result.put("Address Type", addressType);
		
		// Find the querystring
		Pattern patQueryString = Pattern.compile("<form name=\"Form1\" method=\"post\" action=\"([\\w `~!@#\\$%\\^&\\*\\(\\)-_=\\+\\[\\{\\]\\}\\|\\\\;:',<\\.>/\\?]+?)\" id=\"Form1\">");
		Matcher matQueryString = patQueryString.matcher(response);
		if (matQueryString.find()) result.put("Querystring", matQueryString.group(1));
		
		// Find the VIEWSTATE
		Pattern patViewState = Pattern.compile("<input type=\"hidden\" name=\"__VIEWSTATE\" value=\"([\\w `~!@#\\$%\\^&\\*\\(\\)-_=\\+\\[\\{\\]\\}\\|\\\\;:',<\\.>/\\?]+?)\" />");
		Matcher matViewState = patViewState.matcher(response);
		if (matViewState.find()) result.put("Viewstate", matViewState.group(1));
		
		// Find the field name & suggestions
		Pattern patSuggestions = Pattern.compile("<select name=\"(lstClarify(Arrival|Departure))\" size=\"\\d+\" id=\"lstClarify(Arrival|Departure)\" onkeypress=\"return handle(Arrival|Departure)Submit\\(this, event\\)\">(.*?)</select>", Pattern.DOTALL);
		Matcher matSuggestions = patSuggestions.matcher(response);
		if (matSuggestions.find()) {
			// Add the field name to the result arraylist
			result.put("Field Name", matSuggestions.group(2));
			
			// TODO Process the options
			String options = matSuggestions.group(5);
			
			// TODO Return the suggestions
			result.put("Options", options);
		}
		
		// Return result
		return result;
	}

	public BusRide grabBusRideInfo(String busRide) throws ParseException {
		BusRide br = new BusRide();
		DateFormat dfTime = new SimpleDateFormat("h:mm aa");
		
		// Get the route number, name, and direction (inbound/outbound)
		Pattern patRouteNumber = Pattern.compile("<span id=\"_ctl\\d+__ctl\\d+_lblRoute\">\\((\\w+?)\\) ([\\w `~!@#\\$%\\^&\\*\\(\\)-_=\\+\\[\\]\\{\\};:'\\\"\\\\\\|,<\\.>/\\?]+?) (Inbound|Outbound) <");
		Matcher matRouteNumber = patRouteNumber.matcher(busRide);
		if (matRouteNumber.find()) {
			br.setRouteNo(matRouteNumber.group(1));
			br.setRouteName(matRouteNumber.group(2));
			br.setDirection(matRouteNumber.group(3));
		}
		
		// Get quick schedule link
		Pattern patQuickSchedule = Pattern.compile("<a href=\"([\\w\\$-_\\.\\+!\\*'\\(\\), =&\\?%~`@#\\^]+?)\" id=\"_ctl\\d+__ctl\\d+_aRoute\">Quick Schedule \\(<span id=\"_ctl\\d+__ctl\\d+_lblRouteID\">");
		Matcher matQuickSchedule = patQuickSchedule.matcher(busRide);
		if (matQuickSchedule.find()) {
			String tmpLink = matQuickSchedule.group(1);
			if (!tmpLink.matches("^\\w+:/{0,2}.+")) tmpLink = BASE_URL + tmpLink;
			br.setQuickScheduleLink(tmpLink);
		}
		
		// Get PDF schedule link
		Pattern patPdfSchedule = Pattern.compile("</a>&nbsp;&nbsp;---&nbsp;&nbsp;<a href=\"([\\w\\$-_\\.\\+!\\*'\\(\\), =&\\?%~`@#\\^]+?)\" id=\"_ctl\\d+__ctl\\d+_aSchedule\" target=\"_blank\">PDF Schedule \\(<span id=\"_ctl\\d+__ctl\\d+_lblRouteID\\d+\">");
		Matcher matPdfSchedule = patPdfSchedule.matcher(busRide);
		if (matPdfSchedule.find()) {
			String tmpLink = matPdfSchedule.group(1);
			if (!tmpLink.matches("^\\w+:/{0,2}.+")) tmpLink = BASE_URL + tmpLink;
			br.setPdfScheduleLink(tmpLink);
		}

		// Get board stop name & instruction
		Pattern patBoardStopName = Pattern.compile("<span id=\"_ctl\\d+__ctl\\d+_lblGetOn\" class=\"H1\">(Board at|Continue) ([\\w `~!@#\\$%\\^&\\*\\(\\)-_=\\+\\[\\{\\]\\}\\|\\\\;:'\\\",<\\.>/\\?]+?)</span> at");
		Matcher matBoardStopName = patBoardStopName.matcher(busRide);
		if (matBoardStopName.find()) {
			br.setBoardInstruction(matBoardStopName.group(1));
			br.setBoardStopName(matBoardStopName.group(2));
		}
		
		// Get board time
		Pattern patBoardTime = Pattern.compile("<span id=\"_ctl\\d+__ctl\\d+_lblOnTime\">(\\d{1,2}:\\d{2} (PM|AM))</span>. <font align=\"right\"></font>");
		Matcher matBoardTime = patBoardTime.matcher(busRide);
		if (matBoardTime.find()) br.setBoardTime(dfTime.parse(matBoardTime.group(1)));
		
		// Get board stop info link
		Pattern patBoardStopInfoLink = Pattern.compile("<a href=\"([\\w\\$-_\\.\\+!\\*'\\(\\), =&\\?%~`@#\\^]+?)\" id=\"_ctl\\d+__ctl\\d+_aGetOn\">Departure stop information</a><br>");
		Matcher matBoardStopInfoLink = patBoardStopInfoLink.matcher(busRide);
		if (matBoardStopInfoLink.find()) {
			String tmpLink = matBoardStopInfoLink.group(1);
			if (!tmpLink.matches("^\\w+:/{0,2}.+")) tmpLink = BASE_URL + tmpLink;
			br.setBoardStopInfoLink(tmpLink);
		}
		
		// Get board stop id
		Pattern patBoardStopId = Pattern.compile("ATISStopID=(\\d+)[&\\\"]");
		Matcher matBoardStopId = patBoardStopId.matcher(br.getBoardStopInfoLink());
		if (matBoardStopId.find()) br.setBoardStopId(matBoardStopId.group(1));

		// Get board stop latitude
		Pattern patBoardStopLat = Pattern.compile("Lat=(-?\\d*\\.\\d+)[&\\\"]");
		Matcher matBoardStopLat = patBoardStopLat.matcher(br.getBoardStopInfoLink());
		if (matBoardStopLat.find()) br.setBoardStopLat(matBoardStopLat.group(1));
		
		// Get board stop longitude
		Pattern patBoardStopLong = Pattern.compile("Long=(-?\\d*\\.\\d+)[&\\\"]");
		Matcher matBoardStopLong = patBoardStopLong.matcher(br.getBoardStopInfoLink());
		if (matBoardStopLong.find()) br.setBoardStopLong(matBoardStopLong.group(1));
		
		// Get off stop name & instruction
		Pattern patOffStopName = Pattern.compile("<span id=\"_ctl\\d+__ctl\\d+_lblGetOff\" class=\"H1\">(Get [oO]ff at|Stay on through) ([\\w `~!@#\\$%\\^&\\*\\(\\)-_=\\+\\[\\{\\]\\}\\|\\\\;:'\\\",<\\.>/\\?]+?)</span></a> at");
		Matcher matOffStopName = patOffStopName.matcher(busRide);
		if (matOffStopName.find()) {
			br.setOffInstruction(matOffStopName.group(1));
			br.setOffStopName(matOffStopName.group(2));
		}
		
		// Get off time
		Pattern patOffTime = Pattern.compile("<span id=\"_ctl\\d+__ctl\\d+_lblOffTime\">(\\d{1,2}:\\d{2} (PM|AM))</span>.");
		Matcher matOffTime = patOffTime.matcher(busRide);
		if (matOffTime.find()) br.setOffTime(dfTime.parse(matOffTime.group(1)));
		
		// Get off stop info link
		Pattern patOffStopInfoLink = Pattern.compile("<a href=\"([\\w\\$-_\\.\\+!\\*'\\(\\), =&\\?%~`@#\\^]+?)\" id=\"_ctl\\d+__ctl\\d+_aGetOff\">Arrival stop information</a><span id=\"_ctl\\d+__ctl\\d+_lblFinalWalkdir\"></span></asp:Label>");
		Matcher matOffStopInfoLink = patOffStopInfoLink.matcher(busRide);
		if (matOffStopInfoLink.find()) {
			String tmpLink = matOffStopInfoLink.group(1);
			if (!tmpLink.matches("^\\w+:/{0,2}.+")) tmpLink = BASE_URL + tmpLink;
			br.setOffStopInfoLink(tmpLink);
		}
		
		// Get off stop id
		Pattern patOffStopId = Pattern.compile("ATISStopID=(\\d+)[&\\\"]");
		Matcher matOffStopId = patOffStopId.matcher(br.getOffStopInfoLink());
		if (matOffStopId.find()) br.setOffStopId(matOffStopId.group(1));

		// Get off stop latitude
		Pattern patOffStopLat = Pattern.compile("Lat=(-?\\d*\\.\\d+)[&\\\"]");
		Matcher matOffStopLat = patOffStopLat.matcher(br.getOffStopInfoLink());
		if (matOffStopLat.find()) br.setOffStopLat(matOffStopLat.group(1));
		
		// Get off stop longitude
		Pattern patOffStopLong = Pattern.compile("Long=(-?\\d*\\.\\d+)[&\\\"]");
		Matcher matOffStopLong = patOffStopLong.matcher(br.getOffStopInfoLink());
		if (matOffStopLong.find()) br.setOffStopLong(matOffStopLong.group(1));

		// Find out if this ride has detours
		Pattern patIsDetour = Pattern.compile("<A href='([\\w\\$-_\\.\\+!\\*'\\(\\), =&\\?%~`@#\\^]+?)' target='_blank'><img src='images/detour_small.gif' border=0></a></span></b><br/>");
		Matcher matIsDetour = patIsDetour.matcher(busRide);
		if (matIsDetour.find()) {
			// If it is, get detour info link
			String tmpLink = matOffStopId.group(1);
			if (!tmpLink.matches("^\\w+:/{0,2}.+")) tmpLink = BASE_URL + tmpLink;
			br.setDetourInfoLink(tmpLink);
			br.setDetour(true);
		} else {
			br.setDetour(false);
		}

		// Return result
		return br;
	}
	
	@SuppressWarnings("rawtypes")
	public HashMap grabItineraries(String responseText) throws ParseException {
		HashMap<String, Object> result = new HashMap<String, Object>();
		
		// Get trip date
		String strTripDate = "";
		Pattern patTripDate = Pattern.compile("hdnTripDate=(\\d{1,2})%2F(\\d{1,2})%2F(\\d{4})");
		Matcher matTripDate = patTripDate.matcher(responseText);
		if (matTripDate.find()) strTripDate = matTripDate.group(1)+"/"+matTripDate.group(2)+"/"+matTripDate.group(3);		
		
		// Get all itineraries
		Pattern patItineraries = Pattern.compile("<span id=\\\"_ctl\\d+_lblResultsHeader\\\" class=\\\"tablesubheader\\\">(.+?)<TABLE id=\\\"Table1\\\" cellSpacing=\\\"0\\\" cellPadding=\\\"2\\\" width=\\\"100%\\\">", Pattern.DOTALL);
		Matcher matItineraries = patItineraries.matcher(responseText);
		ArrayList<String> arrItinText = new ArrayList<String>();
		while (matItineraries.find()) {
			arrItinText.add(matItineraries.group(1));
		}
		
		// Parse all itineraries
		ArrayList<Itinerary> arrItins = new ArrayList<Itinerary>();
		for (String itin : arrItinText) {
			// Create a new Itinerary object
			Itinerary itinerary = new Itinerary();
			
			// Put the original origin and destination string into the itinerary
			itinerary.setOriginText(actvOrigin.getText().toString());
			itinerary.setDestinationText(actvDestination.getText().toString());
			
			// Get the trip name
			Pattern patTripName = Pattern.compile("^([\\w `~!@#\\$%\\^&\\*\\(\\)-_=\\+\\[\\]\\{\\};:'\\\"\\\\\\|,<\\.>/\\?]+?) - Itinerary \\d+");
			Matcher matTripName = patTripName.matcher(itin);
			if (matTripName.find()) itinerary.setTripName(matTripName.group(1));
			
			// Get the trip time
			Pattern patTripTime = Pattern.compile("<b><span id=\"_ctl\\d+_lblTripTime\"><BR><BR>Total Trip Time is (\\d+) minutes</span></b>");
			Matcher matTripTime = patTripTime.matcher(itin);
			if (matTripTime.find()) itinerary.setTripTime(matTripTime.group(1));
			
			// Set the trip date
			if (strTripDate != null && strTripDate.length()>0) itinerary.setTripDate(strTripDate);
			
			// Get the bus rides
			ArrayList<BusRide> arrBusRides = new ArrayList<BusRide>();
			Pattern patTransferIndicator = Pattern.compile("<span id=\"_ctl\\d+__ctl\\d+_lblTransfer\" class=\"themetextbold\">(Transfer|Stay On The Bus)</span></b>");
			Matcher matTransferIndicator = patTransferIndicator.matcher(itin);
			if (matTransferIndicator.find()) { // Determine if this itinerary includes transfers
				// If it does, grab all bus rides
				matTransferIndicator.reset();
				String[] arrParts = patTransferIndicator.split(itin);
				
				// Loop through all bus rides
				for (String busRide : arrParts) {
					// Grab bus ride particulars
					arrBusRides.add(grabBusRideInfo(busRide));
				}
			} else {
				// If it doesn't, grab bus ride particulars
				arrBusRides.add(grabBusRideInfo(itin));
			}
			itinerary.setBusRides(arrBusRides);
			
			// Get the number of bus rides
			itinerary.setNumOfBusRides(itinerary.getBusRides().size());
			
			// Add this itinerary to the arraylist
			arrItins.add(itinerary);
		}
		
		// Build the result
		result.put("Type", "Itinerary");
		result.put("Itineraries", arrItins);
		
		// Return the result
		return result;
	}
	
	@SuppressWarnings("unchecked")
	public void handleResult(HashMap<String, Object> result) {
		// Determine what to do with the result
		if (result.get("Type").equals("Itinerary")) {
			
			// Create new intent
			Intent intentViewItinerary = new Intent(this, ViewItineraryActivity.class);
			
			// Create and attach the data bundle
			Bundle bundleItinerary = new Bundle();
			if (result.get("Itineraries") != null && result.get("Itineraries").getClass().equals(new ArrayList<Itinerary>().getClass())) {
				bundleItinerary.putSerializable("Itineraries", (ArrayList<Itinerary>) result.get("Itineraries"));
				intentViewItinerary.putExtras(bundleItinerary);
			}
			
			// Directly call the activity
			startActivity(intentViewItinerary);
		} else if (result.get("Type").equals("Suggestions")) {
			// TODO Handle suggestions
			
		} else if (result.get("Type").equals("Error")) {
			displayAlert(this, "Error", result.get("Message").toString(), "Close");
		}
	}
	
	public void toggleAdvancedOptions(View checkBox) {
		if (llAdvancedOptions.getVisibility() == View.GONE) {
			// Make the advanced options box appear
			llAdvancedOptions.setVisibility(View.VISIBLE);
			
			// TODO Adjust the size of the form elements
			
		} else if (llAdvancedOptions.getVisibility() == View.VISIBLE) {
			// Make the advanced option box disappear
			llAdvancedOptions.setVisibility(View.GONE);
			
			// TODO Adjust the size of the form elements
		}
	}
	
	private ArrayList<Itinerary> createFallBackPlan() throws ParseException {
		ArrayList<Itinerary> fallBackPlan = new ArrayList<Itinerary>();
		
		Itinerary itin1 = new Itinerary();
		itin1.setDestinationText("S CRAIG ST");
		itin1.setNumOfBusRides(1);
		itin1.setOriginText("BAUM BLVD & S NEGLEY AVE");
		itin1.setTripDate("5/2/2011");
		itin1.setTripName("BAUM BLVD & S NEGLEY AVE to S CRAIG ST");
		itin1.setTripTime(7);
		ArrayList<BusRide> buses1 = new ArrayList<BusRide>();
		BusRide bus1A = new BusRide();
		bus1A.setBoardInstruction("Board at");
		bus1A.setBoardStopId(6828);
		bus1A.setBoardStopInfoLink("pgStopDetails.aspx?ATISStopID=6828&stop=NEGLEY AVE and CENTRE&Lat=40.457342&Long=-79.934343&Route=71C&Scheduledate=&Direction=I");
		bus1A.setBoardStopLat(40.457342);
		bus1A.setBoardStopLong(-79.934343);
		bus1A.setBoardStopName("NEGLEY AVE & CENTRE");
		bus1A.setBoardTime("5/2/2011 10:49 PM");
		bus1A.setDetour(false);
		bus1A.setDirection("Inbound");
		bus1A.setOffInstruction("Get off at");
		bus1A.setOffStopId(4973);
		bus1A.setOffStopInfoLink("pgStopDetails.aspx?ATISStopID=4973&stop=CRAIG ST and 5TH&Lat=40.447061&Long=-79.949160&Route=71C&Scheduledate=&Direction=I");
		bus1A.setOffStopLat(40.447061);
		bus1A.setOffStopLong(-79.949160);
		bus1A.setOffStopName("CRAIG ST & 5TH");
		bus1A.setOffTime("5/2/2011 10:56 PM");
		bus1A.setPdfScheduleLink("http://www.portauthority.org/paac/apps/pdfs/71C.pdf");
		bus1A.setQuickScheduleLink("TimeTable.aspx?Route=71C&Scheduledate=&Direction=I");
		bus1A.setRouteName("POINT BREEZE DOWNTOWN VIA OAKLAND");
		bus1A.setRouteNo("71C");
		buses1.add(bus1A);
		itin1.setBusRides(buses1);
		fallBackPlan.add(itin1);
		
		Itinerary itin2 = new Itinerary();
		itin2.setDestinationText("S CRAIG ST");
		itin2.setNumOfBusRides(2);
		itin2.setOriginText("BAUM BLVD & S NEGLEY AVE");
		itin2.setTripDate("5/2/2011");
		itin2.setTripName("BAUM BLVD & S NEGLEY AVE to S CRAIG ST");
		itin2.setTripTime(21);
		ArrayList<BusRide> buses2 = new ArrayList<BusRide>();
		BusRide bus2A = new BusRide();
		bus2A.setBoardInstruction("Board at");
		bus2A.setBoardStopId(6824);
		bus2A.setBoardStopInfoLink("pgStopDetails.aspx?ATISStopID=6824&stop=NEGLEY AVE and #370 (BAUM BLVD)&Lat=40.458663&Long=-79.933726&Route=71A&Scheduledate=&Direction=I");
		bus2A.setBoardStopLat(40.458663);
		bus2A.setBoardStopLong(-79.933726);
		bus2A.setBoardStopName("NEGLEY AVE & #370 (BAUM BLVD)");
		bus2A.setBoardTime("5/2/2011 11:04 PM");
		bus2A.setDetour(false);
		bus2A.setDirection("Inbound");
		bus2A.setOffInstruction("Get off at");
		bus2A.setOffStopId(4827);
		bus2A.setOffStopInfoLink("pgStopDetails.aspx?ATISStopID=4827&stop=CENTRE AVE and CRAIG&Lat=40.451969&Long=-79.951777&Route=71A&Scheduledate=&Direction=I");
		bus2A.setOffStopLat(40.451969);
		bus2A.setOffStopLong(-79.951777);
		bus2A.setOffStopName("CENTRE AVE & CRAIG");
		bus2A.setOffTime("5/2/2011 11:09 PM");
		bus2A.setPdfScheduleLink("http://www.portauthority.org/paac/apps/pdfs/71A.pdf");
		bus2A.setQuickScheduleLink("TimeTable.aspx?Route=71A&Scheduledate=&Direction=I");
		bus2A.setRouteName("NEGLEY DOWNTOWN VIA OAKLAND");
		bus2A.setRouteNo("71A");
		buses2.add(bus2A);
		BusRide bus2B = new BusRide();
		bus2B.setBoardInstruction("Board at");
		bus2B.setBoardStopId(4827);
		bus2B.setBoardStopInfoLink("pgStopDetails.aspx?ATISStopID=4827&stop=CENTRE AVE and CRAIG&Lat=40.451969&Long=-79.951777&Route=54&Scheduledate=&Direction=O");
		bus2B.setBoardStopLat(40.451969);
		bus2B.setBoardStopLong(-79.951777);
		bus2B.setBoardStopName("CENTRE AVE & CRAIG");
		bus2B.setBoardTime("5/2/2011 11:23 PM");
		bus2B.setDetour(false);
		bus2B.setDirection("Outbound");
		bus2B.setOffInstruction("Get off at");
		bus2B.setOffStopId(4983);
		bus2B.setOffStopInfoLink("pgStopDetails.aspx?ATISStopID=4983&stop=CRAIG ST and PGH BLIND ASSOCIATION&Lat=40.445866&Long=-79.948930&Route=54&Scheduledate=&Direction=O");
		bus2B.setOffStopLat(40.445866);
		bus2B.setOffStopLong(-79.948930);
		bus2B.setOffStopName("CRAIG ST & PGH BLIND ASSOCIATION");
		bus2B.setOffTime("5/2/2011 11:25 PM");
		bus2B.setPdfScheduleLink("http://www.portauthority.org/paac/apps/pdfs/54.pdf");
		bus2B.setQuickScheduleLink("TimeTable.aspx?Route=54&Scheduledate=&Direction=O");
		bus2B.setRouteName("OAKLAND-SOUTH SIDE SOUTH HILLS JCT VIA PENN & MAIN");
		bus2B.setRouteNo("54");
		buses2.add(bus2B);
		itin2.setBusRides(buses2);
		fallBackPlan.add(itin2);
		
		return fallBackPlan;
	}
	
	public void checkContent(View view) {
		EditText actv = (EditText) view;
		if (actv.getId() == R.id.actvOrigin) {
			if (actv.getText().toString().equals(getResources().getString(R.string.textActvOrigin))) {
				actv.setText("");
			}
		} else if (actv.getId() == R.id.actvDestination) {
			if (actv.getText().toString().equals(getResources().getString(R.string.textActvDestination))) {
				actv.setText("");
			}
		}
	}
}